www.gusucode.com > Phased Array System Toolbox Add-On for Demorad 工具箱matlab源码程序 > Phased Array System Toolbox Add-On for Demorad/shared/utilities/RadarBasebandFileWriter.m
classdef (StrictDefaults)RadarBasebandFileWriter < matlab.System & ... matlab.system.mixin.Propagates & ... matlab.system.mixin.CustomIcon & ... matlab.system.mixin.internal.SampleTime %RadarBasebandFileWriter Write baseband signal input into a file % BBW = RadarBasebandFileWriter creates a baseband file writer System % object, BBW, that writes baseband signals to a binary file. Some % descriptive data about the signals will also be written to the file. % One frame of signals is written at a time. % % BBW = RadarBasebandFileWriter(Name,Value) creates a baseband file % writer System object, BBW, with the specified property Name set to the % specified Value. You can specify additional name-value pair arguments % in any order as (Name1,Value1,...,NameN,ValueN). % % BBW = RadarBasebandFileWriter(FILENAME,FS,FC,MD,Name,Value) creates a % baseband file writer System object, BBW, with the Filename property set % to FILENAME, the SampleRate property set to FS, the CenterFrequency % property set to FC, the Metadata property set to MD, and other % specified property Name set to the specified Value. FILENAME, FS, FC, % and MD are value-only arguments. Specify value-only arguments in order. % They must precede the name-value pairs. You can specify name-value pair % arguments in any order. % % Step method syntax: % % step(BBW, SAMPLES) writes one frame of baseband samples, SAMPLES, to % the baseband file specified by the FILENAME property of BBW. The input % SAMPLES must be a numeric, 2-D matrix with real or complex values. % SAMPLES contains consecutive time samples down each column with one % column per signal channel. % % A baseband file is a specific type of binary file written by % RadarBasebandFileWriter. Baseband signals are typically down-converted % from a center frequency of FC Hz to 0 Hz (baseband), and sampled at a % rate of FS Hz. FS and FC, specified by the SampleRate and % CenterFrequency properties respectively, are recorded when a baseband % file is created. Additional descriptive data about the signals may be % stored in the Metadata property and is written to the baseband file. % % System objects may be called directly like a function instead of using % the step method. For example, y = step(obj, x) and y = obj(x) are % equivalent. % % RadarBasebandFileWriter methods: % % step - Write baseband signal input to a file % release - Allow changes to property values and input characteristics. % Write remaining signals in the baseband file writer object % to the file, and release baseband file writer resources % clone - Create baseband file writer object with same property values % in an unlocked status % isLocked - Locked status (logical) % reset - Reset to the beginning of the baseband file % info - Returns information about the baseband file writer % % RadarBasebandFileWriter properties: % % Filename - Baseband file name % SampleRate - Sample rate (Hz) information for baseband signal % CenterFrequency - Center frequency (Hz) information for baseband signal % Metadata - Additional descriptive data about baseband signal % NumSamplesToWrite - Maximum number of latest samples to write % Copyright 2019 The MathWorks, Inc. %#codegen properties (Nontunable) %Filename Baseband file name % Specify the name of the baseband file as a character row vector. % The file name can include a relative or absolute path. The default % value of this property is 'untitled.bb'. Filename = 'untitled.bb' %SampleRate Sample rate (Hz) % Specify the sample rate for the baseband signal in Hz as a double % precision, real, positive scalar. The default value of this % property is 1 Hz. SampleRate = 1 %CenterFrequency Center frequency (Hz) % Specify the center frequency(ies) for the baseband signal in Hz as % a double precision, real, scalar or row vector with length equal to % the input number of channels. The default value of this property is % 1e8 Hz. CenterFrequency = 1e8 %Metadata Metadata in a structure % Specify any additional descriptive data for the baseband signal as % a structure scalar. The structure's field values can be of any % numeric, logical or character data type with any dimension. The % default value of this property is an empty structure struct(). Metadata = struct() %NumSamplesToWrite Number of latest samples to write % Specify the maximum number of latest samples to write as inf or a % double precision, real, positive integer scalar. When it is set to % inf, all the input samples are stored in the file. The default % value of this property is inf. NumSamplesToWrite = inf end properties (Access = private, Nontunable) pFullFilename pNumBytesPerSamp pSamplesPerFrame pNumChannels pInputDataType pIsCacheNeeded pNumFrmInCache pIsWritingAll end properties (Access = private) pNumBytesInHeader pFID pCache pFrmIdxInCache pNumFrmWritten end properties(Constant, Hidden) MaxCacheSizeInfinite = 1024*1024; % Maximum 1MB cache when writing all MaxCacheSizeFinite = 100*1024*1024; % Maximum 100MB cache when retaining latest VersionInfo = 'BasebandVer1.0.0 '; end methods function obj = RadarBasebandFileWriter(varargin) setProperties(obj,nargin,varargin{:}, ... 'Filename','SampleRate','CenterFrequency','Metadata'); end function set.Filename(obj, fname) propName = 'Filename'; fname = convertStringsToChars(fname); validateattributes(fname, {'char'}, {'row','nonempty'}, ... [class(obj) '.' propName], propName); RadarBasebandFileWriter.validateNameAndExt(fname); obj.Filename = fname; end function set.SampleRate(obj, fs) propName = 'SampleRate'; validateattributes(fs,{'double'}, ... {'scalar','real','positive','finite'}, ... [class(obj) '.' propName], propName); obj.SampleRate = fs; end function set.CenterFrequency(obj, fc) propName = 'CenterFrequency'; validateattributes(fc,{'double'}, ... {'row','real','finite'}, ... [class(obj) '.' propName], propName); obj.CenterFrequency = fc; end function set.Metadata(obj, mData) propName = 'Metadata'; validateattributes(mData,{'struct'}, {'scalar'}, ... [class(obj) '.' propName], propName); metaFields = fieldnames(mData); for i = 1:length(metaFields) validateattributes(mData.(metaFields{i}),... {'numeric','logical','char'}, {'nonsparse','nonempty'}, ... [class(obj) '.' propName], [metaFields{i}, ' field of Metadata']); end obj.Metadata = mData; end function set.NumSamplesToWrite(obj, val) if ~any(isinf(val)) || ~isscalar(val) propName = 'NumSamplesToWrite'; validateattributes(val,{'double'}, ... {'scalar','real','integer','positive'}, ... [class(obj) '.' propName], propName); end obj.NumSamplesToWrite = val; end end methods (Access = protected) function validateInputsImpl(obj, x) validateattributes(x, {'numeric'}, ... {'nonsparse','2d','nonempty','finite'}, class(obj), 'input'); end function setupImpl(obj, x) coder.extrinsic('RadarBasebandFileWriter.getFullFileName', ... 'RadarBasebandFileWriter.getNumBytesPerSample'); obj.pFullFilename = coder.const(... RadarBasebandFileWriter.getFullFileName(obj.Filename)); % Save data attributes obj.pSamplesPerFrame = size(x, 1); obj.pNumChannels = size(x, 2); obj.pInputDataType = class(x); obj.pNumBytesPerSamp = coder.const( ... RadarBasebandFileWriter.getNumBytesPerSample(obj.pInputDataType, true)); if length(obj.CenterFrequency) > 1 && ... length(obj.CenterFrequency) ~= obj.pNumChannels, ... error('The number of input channels must equal the length of the CenterFrequency vector'); end if ~isinf(obj.NumSamplesToWrite) && ... obj.NumSamplesToWrite > obj.MaxCacheSizeFinite/obj.pNumBytesPerSamp/obj.pNumChannels error('NumSamplesToWrite must be inf or less than or equal to %d, given the current input dimension and data type.', ... floor(obj.MaxCacheSizeFinite/obj.pNumBytesPerSamp/obj.pNumChannels)); end % Initialize file and write header initFileAndWriteHeader(obj); % Initialize cache initCache(obj); if ~isempty(coder.target) % Assign properties used in releaseImpl (codegen requirement) resetProperties(obj); end end function resetProperties(obj) % Reset counters obj.pFrmIdxInCache = 0; obj.pNumFrmWritten = 0; % Reset cache if obj.pIsCacheNeeded obj.pCache = complex(zeros( ... [obj.pSamplesPerFrame, obj.pNumChannels, obj.pNumFrmInCache], obj.pInputDataType)); end end function resetImpl(obj) % Skip header and go to the beginning of data part fseek(obj.pFID, obj.pNumBytesInHeader, 'bof'); resetProperties(obj); end function stepImpl(obj, x) if obj.pIsWritingAll && ~obj.pIsCacheNeeded % Write input to file directly rowMajorFileWritting(obj, x); else frmIdxInCache = obj.pFrmIdxInCache; % Fill cache if isreal(x) obj.pCache(:, :, frmIdxInCache+1) = complex(x); else obj.pCache(:, :, frmIdxInCache+1) = x; end % Update frame index in cache, avoid using "mod" for performance if frmIdxInCache == obj.pNumFrmInCache - 1 obj.pFrmIdxInCache = 0; else obj.pFrmIdxInCache = frmIdxInCache + 1; end % Write full cache to file if obj.pIsWritingAll && (obj.pFrmIdxInCache == 0) rowMajorFileWritting(obj, obj.pCache); end end obj.pNumFrmWritten = obj.pNumFrmWritten + 1; end function releaseImpl(obj) numFrmInCache = obj.pNumFrmInCache; frmIdxInCache = obj.pFrmIdxInCache; numFrmWritten = obj.pNumFrmWritten; if ~obj.pIsWritingAll if numFrmWritten < numFrmInCache % When # of samples written is less than NumSamplesToWrite, % write everything in cache to the file and done. rowMajorFileWritting(obj, obj.pCache(:,:,1:frmIdxInCache)); else % First write partial or all samples from the oldest frame in cache numRemSamp = obj.NumSamplesToWrite - (numFrmInCache - 1) * obj.pSamplesPerFrame; rowMajorFileWritting(obj, obj.pCache(end-numRemSamp+1:end,:,frmIdxInCache+1)); % Then write all the rest frames in cache frmIdxToWrite = mod(frmIdxInCache + (1:numFrmInCache-1), numFrmInCache) + 1; rowMajorFileWritting(obj, obj.pCache(:,:,frmIdxToWrite)); end elseif (obj.pIsCacheNeeded) && (obj.pFrmIdxInCache > 0) % Append remaining frames in cache to the file rowMajorFileWritting(obj, obj.pCache(:,:,1:frmIdxInCache)); end % Close file for writing fclose(obj.pFID); obj.pFID = -1; end function s = infoImpl(obj) %info Returns characteristic information about the baseband file writer % S = info(BBW) returns a structure containing characteristic % information, S, about the baseband file writer. A description of the % fields and their values is as follows: % % Filename - The baseband file name with full path % SamplesPerFrame - The input samples per frame, applicable only % when the object is locked % NumChannels - The input number of channels, applicable only % when the object is locked % DataType - The input data type, applicable only when the % object is locked % NumSamplesWritten - The total number of input samples that have % been written to the file. It cannot exceed the % NumSamplesToWrite property value. coder.extrinsic('RadarBasebandFileWriter.getFullFileName'); if ~isLocked(obj) % Have no information to provide other than the filename when the % object is unlocked s.Filename = coder.const(RadarBasebandFileWriter.getFullFileName(obj.Filename)); s.NumSamplesWritten = 0; else s.Filename = obj.pFullFilename; s.SamplesPerFrame = obj.pSamplesPerFrame; s.NumChannels = obj.pNumChannels; s.DataType = obj.pInputDataType; s.NumSamplesWritten = min(obj.pNumFrmWritten * obj.pSamplesPerFrame, ... obj.NumSamplesToWrite); end end function s = saveObjectImpl(obj) s = saveObjectImpl@matlab.System(obj); s.SaveLockedData = false; % Always save the object unlocked end function groups = getPropertyGroups(~) groups = matlab.mixin.util.PropertyGroup({'Filename','SampleRate',... 'CenterFrequency', 'Metadata', 'NumSamplesToWrite'}); end function numIn = getNumInputsImpl(~) numIn = 1; end function flag = isInputSizeLockedImpl(~,~) flag = true; end function flag = isInputComplexityLockedImpl(~,~) flag = false; end % For MATLAB System block function varargout = getInputNamesImpl(~) varargout = {''}; end function icon = getIconImpl(~) icon = sprintf('Radar Baseband\nFile\nWriter'); end end methods (Access = private) function initFileAndWriteHeader(obj) % Open file for writing if isempty(coder.target) [obj.pFID, errMsg] = fopen(obj.pFullFilename, 'w'); if obj.pFID == -1 && ~isempty(errMsg) error('Unable to open baseband file: %s.',obj.pFullFilename) end else obj.pFID = fopen(obj.pFullFilename, 'w'); if obj.pFID == -1 error('Unable to open baseband file.'); end end fs = obj.SampleRate; % Reserve 20 char for version information fwrite(obj.pFID, obj.VersionInfo, 'char'); % Write sample rate into header fwrite(obj.pFID, fs, 'double'); % Write center frequency into header fwrite(obj.pFID, size(obj.CenterFrequency), 'uint32'); fwrite(obj.pFID, obj.CenterFrequency, 'double'); % Write metadata into header metaFields = fieldnames(obj.Metadata); numMetaFields = length(metaFields); fwrite(obj.pFID, numMetaFields, 'uint32'); % Number of fields in metadata for i = 1:numMetaFields % Write each metadata field one-by-one thisValue = obj.Metadata.(metaFields{i}); thisClass = class(thisValue); fwrite(obj.pFID, length(metaFields{i}), 'uint32' ); % Field: Length fwrite(obj.pFID, metaFields{i}, 'char' ); % Field: Name fwrite(obj.pFID, getDataTypeID(thisClass), 'uint8' ); % Value: Data type fwrite(obj.pFID, uint8(isreal(thisValue)), 'uint8' ); % Value: Complexity fwrite(obj.pFID, ndims(thisValue), 'uint8' ); % Value: Num of dim fwrite(obj.pFID, size(thisValue), 'uint32' ); % Value: Dim if isa(thisValue, 'logical') classToWrite = 'uint8'; else classToWrite = thisClass; end fwrite(obj.pFID, real(thisValue), classToWrite); % Value: Real part if ~isreal(thisValue) fwrite(obj.pFID, imag(thisValue), classToWrite); % Value: Imaginary part end end % Write data information into header fwrite(obj.pFID, getDataTypeID(obj.pInputDataType), 'uint8'); fwrite(obj.pFID, obj.pNumBytesPerSamp, 'uint32'); fwrite(obj.pFID, obj.pSamplesPerFrame, 'uint32'); fwrite(obj.pFID, obj.pNumChannels, 'uint32'); % Store current file pointer, which is where data part begins obj.pNumBytesInHeader = ftell(obj.pFID); end function initCache(obj) sampPerFrm = obj.pSamplesPerFrame; numChan = obj.pNumChannels; % Determine # of frames in cache obj.pIsWritingAll = isinf(obj.NumSamplesToWrite); if obj.pIsWritingAll obj.pNumFrmInCache = floor(obj.MaxCacheSizeInfinite/... obj.pNumBytesPerSamp/numChan/sampPerFrm); % >= 0 else % Hold all the data in cache until release call obj.pNumFrmInCache = ceil(obj.NumSamplesToWrite /sampPerFrm); % >= 1 end obj.pIsCacheNeeded = ~obj.pIsWritingAll || (obj.pNumFrmInCache >= 2); end function rowMajorFileWritting(obj, data) % Most efficient way to write cache into file in a row major fashion if ~isempty(data) count = fwrite(obj.pFID, cat(1, permute(real(data), [2 1 3]), ... permute(imag(data), [2 1 3])), obj.pInputDataType); if count < 2*numel(data), ... error('Data lost in file writing.'); end end end end methods(Static, Access = protected) function header = getHeaderImpl header = matlab.system.display.Header('RadarBasebandFileWriter', ... 'ShowSourceLink', true, ... 'Title', 'Baseband File Writer', 'Text', ... 'Write baseband signal input into a file.'); end function group = getPropertyGroupsImpl param = matlab.system.display.Section(... 'Title', 'Parameters',... 'PropertyList', {'Filename','CenterFrequency', ... 'Metadata', 'NumSamplesToWrite'}); browseAction = matlab.system.display.Action(@RadarBasebandFileWriter.onBrowseActionCalled, ... 'Label', 'Browse...','Placement','CenterFrequency','Alignment', 'right'); param.Actions = browseAction; group = param; end function onBrowseActionCalled(actionData, ~) [fname, pathname] = uiputfile('*.bb', 'Pick a baseband file'); if ischar(fname) fullname = fullfile(pathname, fname); h = actionData.SystemHandle; if isa(h, 'matlab.System') h.Filename = fullname; else set_param(h, 'Filename', fullname); end end end end methods(Static, Hidden) function validateNameAndExt(fname) [~, name, ext] = fileparts(fname); % Check file name if ~isempty(regexp(name, '[/\*:?"<>|]', 'once')) || ... isempty(name) error('Invalid file name.'); end % Check file extension if ~isempty(regexp(ext, '[/\*:?"<>|]', 'once')) error('Invalid file extension.'); end end function fullName = getFullFileName(fname) [pathStr, name, ext] = fileparts(fname); if ~isempty(pathStr) % Check path if ~isdir(pathStr), ... error('Path %s does not exist.',pathStr); end wd = cd(pathStr); fullName = [pwd filesep name ext]; cd(wd); else fullName = [pwd filesep name ext]; end end function numBytes = getNumBytesPerSample(dataType, isDataComplex) temp = ones(1, 1, dataType); %#ok<NASGU> s = whos('temp'); numBytes = s.bytes * (1 + isDataComplex); end end end % Local helper functions function id = getDataTypeID(dType) switch dType case 'double' id = uint8(1); case 'single' id = uint8(2); case 'int8' id = uint8(3); case 'int16' id = uint8(4); case 'int32' id = uint8(5); case 'int64' id = uint8(6); case 'uint8' id = uint8(7); case 'uint16' id = uint8(8); case 'uint32' id = uint8(9); case 'uint64' id = uint8(10); case 'logical' id = uint8(11); otherwise % 'char' id = uint8(12); end end % [EOF]